/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkRtmpDemuxer.h"
#include "MkAmf/MkAmf.h"
#include "MkUtil/MkLog.h"

Uint32 CMkRtmpDemuxer::Parse(Uint32 nChunkSize, const Uint8* pBuf, Uint32 nLen, Uint32& nUsed, Uint8** pBody, MkRtmpHeader& RtmpHeader)
{
    Uint32 ErrorCode = NoneError;
    Uint32 nHeaderUsed = 0;

    ErrorCode = ParseRtmpHeader(pBuf, nLen, nHeaderUsed, RtmpHeader);
    if (NoneError != ErrorCode) {
        //MkWarningLog("parse rtmp header failed\n");
        return ErrorCode;
    }
    Uint32 nToRead = RtmpHeader.BodySize - RtmpHeader.ByteReaded;
    if (nLen - nHeaderUsed < nToRead) {
        return NotHasEnoughBuffer;
    }
    if (nToRead > nChunkSize) {
        nToRead = nChunkSize;
    }
    *pBody = (Uint8*)pBuf + nHeaderUsed;
    nUsed = nHeaderUsed + nToRead;
    RtmpHeader.ByteReaded = nToRead;
    const Uint8* pOffset = nullptr;
    Uint32 nCurlen = 0;
    MkRtmpHeader TmpPacket;
    while (RtmpHeader.ByteReaded < RtmpHeader.BodySize) {
        pOffset = pBuf + nUsed;
        nCurlen = nLen - nUsed;
        TmpPacket.TimeStamp = RtmpHeader.TimeStamp > 0xffffff ? 0xffffff : 0;
        ErrorCode = ParseRtmpHeader(pOffset, nCurlen, nHeaderUsed, TmpPacket);
        MkCheckErrorLog(ErrorCode, "parse header failed\n");
        nToRead = RtmpHeader.BodySize - RtmpHeader.ByteReaded;
        if (nToRead > nChunkSize) {
            nToRead = nChunkSize;
        }
        memmove(*pBody + RtmpHeader.ByteReaded, pOffset + nHeaderUsed, nToRead);
        nUsed += nHeaderUsed + nToRead;
        RtmpHeader.ByteReaded += nToRead;
    }
    return NoneError;
}

Uint32 CMkRtmpDemuxer::ParseRtmpHeader(const Uint8* pBuf, Uint32 nLen, Uint32& nUsed, MkRtmpHeader& RtmpHeader)
{
    const Uint8 *pBufferEnd = pBuf + nLen;
    Uint8 Head = 0;
    const Uint8 *pOffset = CMkAmf::DecodeUint8(pBuf, pBufferEnd, Head);
    MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode rtmp header failed\n");

    RtmpHeader.HeaderType = (MkRtmpHeaderFmtType)((Head & 0xC0) >> 6);
    RtmpHeader.ChunkId = Head & 0x3F;
    if (0 == RtmpHeader.ChunkId) {
        //chunk id is small than 319
        Uint8 TmpChunkSize = 0;
        pOffset = CMkAmf::DecodeUint8(pOffset, pBufferEnd, TmpChunkSize);
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode chunk size failed\n");
        RtmpHeader.ChunkId = TmpChunkSize + 64;
    } else if (1 == RtmpHeader.ChunkId) {
        Uint16 TmpChunkSize = 0;
        pOffset = CMkAmf::DecodeUint16(pOffset, pBufferEnd, TmpChunkSize);
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode chunk size failed\n");
        RtmpHeader.ChunkId += TmpChunkSize;
    }

    if (RtmpHeader.HeaderType < MkRtmpHeaderFmt3)  // add time stamp
    {
        pOffset = CMkAmf::DecodeUint24(pOffset, pBufferEnd, RtmpHeader.TimeStamp);
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode timestamp failed\n");
    }
    if (RtmpHeader.HeaderType < MkRtmpHeaderFmt2) {
        pOffset = CMkAmf::DecodeUint24(pOffset, pBufferEnd, RtmpHeader.BodySize);  //message len
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode msg len end\n");
        Uint8 MsgType = 0;
        pOffset = CMkAmf::DecodeUint8(pOffset, pBufferEnd, MsgType);
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode msg type failed\n");
        RtmpHeader.CmdType = (MkRtmpCommandType)MsgType;
    }
    if (RtmpHeader.HeaderType < MkRtmpHeaderFmt1) {
        pOffset = CMkAmf::DecodeUint32(pOffset, pBufferEnd, RtmpHeader.StreamId);
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode stream id failed");
    }
    if (0xFFFFFF == RtmpHeader.TimeStamp) {
        pOffset = CMkAmf::DecodeUint32(pOffset, pBufferEnd, RtmpHeader.TimeStamp);
        MkCheckErrorPoint(pOffset, NotHasEnoughBuffer, "decode timestamp failed");
    }
    nUsed = pOffset - pBuf;
    return NoneError;
}